using System;
using System.Configuration;
using System.Xml;
using System.Xml.XPath;
using System.Diagnostics;
using System.Collections;
using System.Collections.Specialized;

namespace Copperline.Escriba.Config
{
	/// <summary>
	/// Summary description for ConfigurationSectionHandler.
	/// </summary>
	public class ConfigSectionHandler : IConfigurationSectionHandler
	{
		/// <summary>
		/// Parses the XML of the configuration section.
		/// </summary>
		/// <param name="parent">The configuration settings in a corresponding parent configuration section</param>
		/// <param name="input"></param>
		/// <param name="node">The <see cref="XmlNode"/> that contains the configuration information from the configuration file. Provides direct access to the XML contents of the configuration section</param>
		/// <returns></returns>
		/// <remarks> The returned object is added to the configuration collection and is accessed by <c>GetConfig</c></remarks>
		public Object Create(Object parent, Object input, XmlNode node)
		{
			UserLogger root = new UserLogger();
			XmlNodeList nodelist = node.SelectNodes( String.Format("/{0}/{1}", ConfigHelper.PARENT_ELEMENT, ConfigHelper.LOGGER_ELEMENT ) );
			foreach( XmlNode logger in nodelist )
			{
				XPathNavigator xpn = logger.CreateNavigator();
				ConfigHelper.ConfigureLogger( xpn, ref root );
			}

			LoggerFactory.Create( root.Nested );
			return root.Nested;
		}
	}

	internal class NameValuePair
	{
		public NameValuePair( string name, string val )
		{
			this.name = name;
			this.val = val;
		}
		public override string ToString()
		{
			return "(" + name + "," + val + ")";
		}

		private string name;
		public string Name
		{
			get { return name; }
			set { name = value; }
		}
		private string val;
		public string Value
		{
			get { return val; }
			set { val = value; }
		}
	}

	internal class ConfigHelper
	{
		public  const string NAMESPACE = "Copperline.Escriba";
		public  const string NAME_ATTR = "name";
		public  const string VALUE_ATTR = "value";
		public  const string TYPE_ATTR = "type";
		public  const string PROPERTY_ELEMENT = "property";
		public  const string FILTER_ELEMENT = "filter";
		public  const string FORMATTER_ELEMENT = "formatter";
		public  const string LOGGER_ELEMENT = "logger";
		public  const string PARENT_ELEMENT = "escriba";

		public static void ConfigureLogger( XPathNavigator navLogger, ref UserLogger parent )
		{
			if( navLogger.HasAttributes )
			{
				UserLogger ul = new UserLogger();
				string key = "";
				string val = "";
				if( GetKeyValue( navLogger, ConfigHelper.NAME_ATTR, ref key, ConfigHelper.TYPE_ATTR, ref val ) )
				{
					ul.Name = key;
					ul.Type = val;
					if( navLogger.HasChildren )
					{
						navLogger.MoveToFirstChild();
						ConfigureComponents( navLogger, ref ul );
					}

					parent.Nested.Add( ul );
				}
			}
		}

		public static void ConfigureComponents( XPathNavigator navLogger, ref UserLogger ul )
		{
			do
			{
				string key = "";
				string val = "";
				switch( navLogger.Name )
				{
					case ConfigHelper.LOGGER_ELEMENT:
						if( navLogger.HasAttributes )
						{
							ConfigureLogger( navLogger.Clone(), ref ul );
						}
						break;

					case ConfigHelper.PROPERTY_ELEMENT:
						if( GetKeyValue( navLogger, ConfigHelper.NAME_ATTR, ref key, ConfigHelper.VALUE_ATTR, ref val ) )
						{
							ul.Properties.Add( key, new NameValuePair(key,val) );
						}
						break;

					case ConfigHelper.FILTER_ELEMENT:
						break;

					case ConfigHelper.FORMATTER_ELEMENT:
						break;

					default:
						break;
				}
			} while( navLogger.MoveToNext() );
		}
		//Helper method for retrieving enum values from XmlNode.
		public static XmlNode GetEnumValue( XmlNode node, string attribute, Type enumType, ref int val)
		{
			XmlNode a = node.Attributes.RemoveNamedItem( attribute );
			if( a == null )
				throw new ConfigurationException( "Attribute required: " + attribute );

			if( Enum.IsDefined( enumType, a.Value ) )
				val = (int)Enum.Parse( enumType, a.Value );

			else
				throw new ConfigurationException( "Invalid " + attribute + ": '" + a.Value + "'", a);

			return a;
		}

		//Helper method for retrieving string values from xmlnode.
		public static XmlNode GetStringValue( XmlNode node, string attribute, ref string val)
		{
			XmlNode a = node.Attributes.RemoveNamedItem( attribute );
			if( a == null )
				throw new ConfigurationException("Attribute required: " + attribute);

			else
				val = a.Value;

			return a;
		}

		private static bool GetKeyValue( XPathNavigator nav, string keyName, ref string keyValue, string valName, ref string valValue )
		{
			keyValue = nav.GetAttribute( keyName, nav.NamespaceURI );
			valValue = nav.GetAttribute( valName, nav.NamespaceURI );

			if( ( keyValue == null  || keyValue.Equals(String.Empty) ) )
				return false;

			if( ( valValue == null  || valValue.Equals(String.Empty) ) )
				return false;

			return true;
		}
	}
}
